/************************************************************************/
/*																		*/
/*			Autor: Dobromir Todorov										*/
/*			Email: dobromir.todorov.todorov@gmail.com                   */
/*																		*/
/************************************************************************/

//#include "stdafx.h"
#include "ObjectControl.h"
//move widgets
void ObjectControl::init()
{
	using namespace Ogre;
	try
	{
		//sceneNodes
		node = mSceneMgr->createSceneNode("move_widget");
		SceneNode* node_x = node->createChildSceneNode("move_widget_x");node_x->showBoundingBox(false);
		SceneNode* node_y = node->createChildSceneNode("move_widget_y");node_y->showBoundingBox(false);
		SceneNode* node_z = node->createChildSceneNode("move_widget_z");node_z->showBoundingBox(false);

		node_x->roll(Degree(90));
		node_z->pitch(Degree(-90));

		node = mSceneMgr->createSceneNode("rotate_widget");
		SceneNode* node_rot_x = node->createChildSceneNode("rotate_widget_x");node_rot_x->showBoundingBox(false);
		SceneNode* node_rot_y = node->createChildSceneNode("rotate_widget_y");node_rot_y->showBoundingBox(false);
		SceneNode* node_rot_z = node->createChildSceneNode("rotate_widget_z");node_rot_z->showBoundingBox(false);
		node_rot_x->roll(Degree(90));
		node_rot_z->pitch(Degree(90));

		node = mSceneMgr->createSceneNode("scale_widget");
		SceneNode* node_scale_x = node->createChildSceneNode("scale_widget_x");node_scale_x->showBoundingBox(false);
		SceneNode* node_scale_y = node->createChildSceneNode("scale_widget_y");node_scale_y->showBoundingBox(false);
		SceneNode* node_scale_z = node->createChildSceneNode("scale_widget_z");node_scale_z->showBoundingBox(false);
		SceneNode* node_scale_xyz = node->createChildSceneNode("scale_widget_xyz");node_scale_xyz->showBoundingBox(false);

		node_scale_x->roll(Degree(90));
		node_scale_z->pitch(Degree(-90));

		//Materials

		MaterialPtr blue = MaterialManager::getSingleton().create("blue_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		blue->setSelfIllumination(0,0,1);
		blue->setAmbient(0,0,0);
		blue->setSpecular(0,0,0,1);
		blue->setDiffuse(0.5,0.5,0.5,1);
		MaterialPtr red = MaterialManager::getSingleton().create("red_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		red->setSelfIllumination(1,0,0);
		red->setAmbient(0,0,0);
		red->setSpecular(0,0,0,1);
		red->setDiffuse(0.5,0.5,0.5,1);
		MaterialPtr green = MaterialManager::getSingleton().create("green_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		green->setSelfIllumination(0,1,0);
		green->setAmbient(0,0,0);
		green->setSpecular(0,0,0,1);
		green->setDiffuse(0.5,0.5,0.5,1);

		MaterialPtr yellow = MaterialManager::getSingleton().create("yellow_widget",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
		yellow->setSelfIllumination(1,1,0);
		yellow->setAmbient(0,0,0);
		yellow->setSpecular(0,0,0,1);
		yellow->setDiffuse(0.5,0.5,0.5,1);

		//Entities
		Entity* entityZ = mSceneMgr->createEntity("move_widget_z", "arrow.mesh");
		entityZ->setQueryFlags(0x1);
		Entity* entityX = mSceneMgr->createEntity("move_widget_x", "arrow.mesh");
		entityX->setQueryFlags(0x1);
		Entity* entityY = mSceneMgr->createEntity("move_widget_y", "arrow.mesh");
		entityY->setQueryFlags(0x1);

		Entity* entity_rot_Z = mSceneMgr->createEntity("rotate_widget_z", "torus.mesh");
		entity_rot_Z->setQueryFlags(0x1);
		Entity* entity_rot_X = mSceneMgr->createEntity("rotate_widget_x", "torus.mesh");
		entity_rot_X->setQueryFlags(0x1);
		Entity* entity_rot_Y = mSceneMgr->createEntity("rotate_widget_y", "torus.mesh");
		entity_rot_Y->setQueryFlags(0x1);

		Entity* entity_scale_Z = mSceneMgr->createEntity("scale_widget_z", "ScaleArrow.mesh");
		entity_scale_Z->setQueryFlags(0x1);
		Entity* entity_scale_X = mSceneMgr->createEntity("scale_widget_x", "ScaleArrow.mesh");
		entity_scale_X->setQueryFlags(0x1);
		Entity* entity_scale_Y = mSceneMgr->createEntity("scale_widget_y", "ScaleArrow.mesh");
		entity_scale_Y->setQueryFlags(0x1);
		Entity* entity_scale_XYZ = mSceneMgr->createEntity("scale_widget_xyz", "ScaleUniform.mesh");
		entity_scale_XYZ->setQueryFlags(0x1);

		MovablePlane *mPlane;
		mPlane = new MovablePlane("dummy_plane_x");
		mPlane->normal = Vector3::UNIT_X;
		MeshManager::getSingleton().createPlane("dummy_plane_x",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 3000, 3000, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y);
		mSceneMgr->createEntity( "dummy_plane_x", "dummy_plane_x" );
		mSceneMgr->getEntity("dummy_plane_x")->setVisible(false);

		mPlane = new MovablePlane("dummy_plane_z");
		mPlane->normal = Vector3::UNIT_Z;
		MeshManager::getSingleton().createPlane("dummy_plane_z",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 3000, 3000, 1, 1, true, 1, 1, 1, Vector3::UNIT_Y);

		mSceneMgr->createEntity( "dummy_plane_z", "dummy_plane_z" );
		mSceneMgr->getEntity("dummy_plane_z")->setVisible(false);

		mPlane = new MovablePlane("dummy_plane_y");
		mPlane->normal = Vector3::UNIT_Y;
		MeshManager::getSingleton().createPlane("dummy_plane_y",ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,*mPlane, 3000, 3000, 1, 1, true, 1, 1, 1, Vector3::UNIT_X);
		mSceneMgr->createEntity( "dummy_plane_y", "dummy_plane_y" );
		mSceneMgr->getEntity("dummy_plane_y")->setVisible(false);

		planeXNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		planeXNode->attachObject(mSceneMgr->getEntity("dummy_plane_x"));

		planeYNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		planeYNode->attachObject(mSceneMgr->getEntity("dummy_plane_y"));

		planeZNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
		planeZNode->attachObject(mSceneMgr->getEntity("dummy_plane_z"));

		//ZZ components 
		entityZ->setCastShadows(false);
		entityZ->setMaterialName("blue_widget");
		node_z->attachObject(entityZ);

		entity_rot_Z->setCastShadows(false);
		entity_rot_Z->setMaterialName("blue_widget");
		node_rot_z->attachObject(entity_rot_Z);

		entity_scale_Z->setCastShadows(false);
		entity_scale_Z->setMaterialName("blue_widget");
		node_scale_z->attachObject(entity_scale_Z);

		//XX components
		entityX->setCastShadows(false);
		entityX->setMaterialName("red_widget");
		node_x->attachObject(entityX);

		entity_rot_X->setCastShadows(false);
		entity_rot_X->setMaterialName("red_widget");
		node_rot_x->attachObject(entity_rot_X);

		entity_scale_X->setCastShadows(false);
		entity_scale_X->setMaterialName("red_widget");
		node_scale_x->attachObject(entity_scale_X);

		//YY components
		entityY->setCastShadows(false);
		entityY->setMaterialName("green_widget");
		node_y->attachObject(entityY);

		entity_rot_Y->setCastShadows(false);
		entity_rot_Y->setMaterialName("green_widget");
		node_rot_y->attachObject(entity_rot_Y);

		entity_scale_Y->setCastShadows(false);
		entity_scale_Y->setMaterialName("green_widget");
		node_scale_y->attachObject(entity_scale_Y);

		entity_scale_XYZ->setCastShadows(false);
		entity_scale_XYZ->setMaterialName("yellow_widget");
		node_scale_xyz->attachObject(entity_scale_XYZ);

		mRaySceneQuery = mSceneMgr->createRayQuery(Ogre::Ray());
	}
	catch(Ogre::Exception e)
	{
		std::cout << "An exception has occured while creating widgets: " << e.getFullDescription().c_str() << std::endl;
	}
}

Ogre::MovableObject* ObjectControl::getNode(float mouseScreenX, float mouseScreenY)
{
	using namespace Ogre;
	Ray mouseRay =mCamera->getCameraToViewportRay(mouseScreenX,mouseScreenY);
	mRaySceneQuery->setRay(mouseRay);
	mRaySceneQuery->setSortByDistance(true);
	RaySceneQueryResult &result = mRaySceneQuery->execute();

	Ogre::MovableObject *closestObject = NULL;
	Real closestDistance = 100000;

	Ogre::RaySceneQueryResult::iterator rayIterator= result.begin();

	for(rayIterator; rayIterator != result.end(); rayIterator++ ) 
	{
		if ((*rayIterator).movable !=NULL && closestDistance>(*rayIterator).distance && 
			(*rayIterator).movable->getMovableType() == "Entity")
		{
			if( (*rayIterator ).movable->getName()!="dummy_plane_x" &&
				(*rayIterator ).movable->getName()!="dummy_plane_y" &&
				(*rayIterator ).movable->getName()!="dummy_plane_z" &&
				ObjIsMoveable((*rayIterator).movable))
			{
				closestObject = ( *rayIterator ).movable;
				closestDistance = ( *rayIterator ).distance;

			}
		}
	}
	mRaySceneQuery->clearResults();
	return closestObject;
}
void ObjectControl::selectObjectForEdit(const char* idObject, char* type)
{
	using namespace Ogre;
	try
	{
		widget = mSceneMgr->getSceneNode(type);
		node = mSceneMgr->getSceneNode(idObject);
		widget->setScale(0.3,0.3,0.3);
		Vector3 objPos = Vector3(node->_getWorldAABB().getCenter());
		mSceneMgr->getRootSceneNode()->removeChild(widget);
		mSceneMgr->getRootSceneNode()->addChild(widget);
		Vector3 raytoCam = objPos-mCamera->getRealPosition();
		raytoCam.normalise();
		raytoCam*=20;
		widget->setPosition(mCamera->getRealPosition()+raytoCam);
		widget->setVisible(true);
	}
	catch(...)
	{

	}
}
void ObjectControl::update()
{
	using namespace Ogre;
	if(widget&&node)
	{
		Vector3 objPos = Vector3(node->_getWorldAABB().getCenter());
		mSceneMgr->getRootSceneNode()->removeChild(widget);
		mSceneMgr->getRootSceneNode()->addChild(widget);
		Vector3 raytoCam = objPos-mCamera->getRealPosition();
		raytoCam.normalise();
		raytoCam*=20;
		widget->setPosition(mCamera->getRealPosition()+raytoCam);
		widget->setVisible(true);

		planeXNode->setPosition(objPos);
		planeYNode->setPosition(objPos);
		planeZNode->setPosition(objPos);
	}
}

void ObjectControl::mousePressed(const OIS::MouseEvent &arg)
{
	using namespace Ogre;
	MovableObject* nodeM = getNode(arg.state.X.abs/float(arg.state.width),arg.state.Y.abs/float(arg.state.height)); 

	if(nodeM != NULL)
	{
		String name = nodeM->getParentSceneNode()->getName();

		if (name=="move_widget_x")
		{
			Vector3 objPos = Vector3(node->_getWorldAABB().getCenter());
			planeXNode->setPosition(objPos);
			planeYNode->setPosition(objPos);
			planeZNode->setPosition(objPos);

			currntOperation=MoveX;                
		}
		else if (name=="move_widget_y")
		{
			Vector3 objPos = Vector3(node->_getWorldAABB().getCenter());			
			planeXNode->setPosition(objPos);
			planeYNode->setPosition(objPos);
			planeZNode->setPosition(objPos);
			currntOperation=MoveY; 
		}
		else if (name=="move_widget_z")
		{
			Vector3 objPos = Vector3(node->_getWorldAABB().getCenter());						
			planeXNode->setPosition(objPos);
			planeYNode->setPosition(objPos);
			planeZNode->setPosition(objPos);
			currntOperation=MoveZ;                 
		}
		else if (name=="rotate_widget_x")
		{
			currntOperation=RotateX;                 
		}
		else if (name=="rotate_widget_y")
		{
			currntOperation=RotateY;                 
		}
		else if (name=="rotate_widget_z")
		{
			currntOperation=RotateZ;                 
		}
		else if (name=="scale_widget_x")
		{
			currntOperation=ScaleX;                 
		}
		else if (name=="scale_widget_y")
		{
			currntOperation=ScaleY;                 
		}
		else if (name=="scale_widget_z")
		{
			currntOperation=ScaleZ;                 
		}
		else if (name=="scale_widget_xyz")
		{
			currntOperation=ScaleXYZ;                 
		}
		else 
		{
			if (ObjIsMoveable(nodeM))
			{
				if(currentMode==Move_Mode) selectObjectForEdit(name.c_str(),"move_widget");
				else if(currentMode==Rotate_Mode) selectObjectForEdit(name.c_str(),"rotate_widget");
				else if(currentMode==Scale_Mode) selectObjectForEdit(name.c_str(),"scale_widget");
			}
		}
	}
	else
	{
		if(widget) mSceneMgr->getRootSceneNode()->removeChild(widget);
	}
}
void ObjectControl::mouseReleased()
{
	currntOperation=0;
}
void ObjectControl::mouseDragged (const OIS::MouseEvent &arg) 
{
	using namespace Ogre;
	Ray mouseRay =mCamera->getCameraToViewportRay(arg.state.X.abs/float(arg.state.width),arg.state.Y.abs/float(arg.state.height));
	mRaySceneQuery->setRay(mouseRay);
	mRaySceneQuery->setSortByDistance(true);
	RaySceneQueryResult &result = mRaySceneQuery->execute();

	Ogre::MovableObject *closestObject = NULL;
	Real closestDistance = 100000;

	Ogre::RaySceneQueryResult::iterator rayIterator= result.begin();

	for(rayIterator; rayIterator != result.end(); rayIterator++ ) 
	{
		if ((*rayIterator).movable !=NULL && 
			closestDistance>(*rayIterator).distance && 
			(*rayIterator).movable->getMovableType() == "Entity")
		{

			closestObject = ( *rayIterator ).movable;
			Ogre::String name = closestObject->getName();
			if (name!="dummy_plane_x" && name!="dummy_plane_z"&& name!="dummy_plane_y")
				continue;

			if (currntOperation==MoveX)
			{
				if (name!="dummy_plane_y") continue;
				Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance);
				node->setPosition(newpos.x,node->getPosition().y,node->getPosition().z);
				update();
				return;
			}
			else if (currntOperation==MoveY)
			{
				if(name=="dummy_plane_y") continue; 
				Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance);
				node->setPosition(node->getPosition().x,newpos.y-node->_getWorldAABB().getHalfSize().y,node->getPosition().z);
				update();
				return;
			}
			else if (currntOperation==MoveZ)
			{
				if(name!="dummy_plane_y") continue; 
				Ogre::Vector3 newpos = mouseRay.getPoint((*rayIterator).distance);
				node->setPosition(node->getPosition().x,node->getPosition().y,newpos.z);				
				update();
				return;
			}
			else if (currntOperation==RotateX)
			{
				float rotateFactor = 1*arg.state.Y.rel;

				node->pitch(Ogre::Degree(rotateFactor),Node::TS_WORLD);
				update();
				return;
			}
			else if (currntOperation==RotateY)
			{
				float rotateFactor = 1*arg.state.X.rel;
				node->yaw(Ogre::Degree(rotateFactor),Node::TS_WORLD);
				update();
				return;
			}
			else if (currntOperation==RotateZ)
			{
				float rotateFactor = 1*arg.state.Y.rel;
				node->roll(Ogre::Degree(rotateFactor),Node::TS_WORLD);
				update();
				return;
			}
			else if (currntOperation==ScaleX)
			{
				float scaleFactor = 1+0.001*arg.state.X.rel;
				node->scale(scaleFactor,1,1);
				update();
				return;
			}
			else if (currntOperation==ScaleY)
			{
				float scaleFactor = 1-0.001*arg.state.Y.rel;
				node->scale(1,scaleFactor,1);
				update();
				return;
			}
			else if (currntOperation==ScaleZ)
			{
				float scaleFactor = 1+0.001*arg.state.X.rel;
				node->scale(1,1,scaleFactor);
				update();
				return;
			}
			else if (currntOperation==ScaleXYZ)
			{
				float scaleFactor = 1+0.001*arg.state.X.rel;
				node->scale(scaleFactor,scaleFactor,scaleFactor);
				update();
				return;
			}
		}	
	}
	mRaySceneQuery->clearResults();
}
void ObjectControl::switchMode(int mode)
{
	currentMode=mode;
	if(widget)
	{	
		mSceneMgr->getRootSceneNode()->removeChild(widget);
		char* type; 
		if(mode==Move_Mode) type="move_widget";
		else if(mode==Rotate_Mode) type="rotate_widget";
		else if(mode==Scale_Mode) type= "scale_widget";
		widget = mSceneMgr->getSceneNode(type);
		widget->setScale(0.3,0.3,0.3);
		update();
	}
}

bool ObjectControl::ObjIsMoveable( Ogre::MovableObject* obj )
{
	for (unsigned int i=0; i<mMovableFlagSet.size(); i++)
	{
		if (obj->getQueryFlags() == mMovableFlagSet[i])
			return true;
	}
	return false;
}

void ObjectControl::RemoveMavableFlag( unsigned int iflag )
{
	for (unsigned int i=0; i<mMovableFlagSet.size(); i++)
	{
		if (mMovableFlagSet[i] == iflag)
		{
			mMovableFlagSet.erase(mMovableFlagSet.begin()+iflag);
			break;
		}
	}
}

void ObjectControl::RemoveSelection()
{
	node = NULL;
	if(widget) mSceneMgr->getRootSceneNode()->removeChild(widget);
}
